#Authors: [INSERT NAMES HERE]
#Author Date: 
#Purpose: The purpose of this notebook is to house all data set transformation, cleansing, visualization, statistical analysis, and note-taking for the 2025 CU Athletic Department Sports Science Internship Program

#LAST UPDATED: 

#Including helpful libraries
library(tidyverse)
library(readxl)
library(aod)

Data Cleaning

#loading in the Catapult data to look at sprinting values
Catapult_Session <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Catapult Session - Outdoor FB.csv")

#loading in the Historical Running data to look at running imbalance values
Historical_Running <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Compiled Historical Running Imbalance FB.csv")

#loading in the Incident Report to look at HSIs
Incident_Report <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Incident Report FB IDs.csv")
Catapult_Session_clean <- Catapult_Session %>%
  #putting the date as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only selecting important columns for this analysis
  select(anon_id, Date, Age, Primary.Position, Total.Distance, Period.Name, Total.Duration..min., Velocity.Band.1.Total.Distance, Velocity.Band.2.Total.Distance, Velocity.Band.3.Total.Distance, Velocity.Band.4.Total.Distance, Velocity.Band.5.Total.Distance, Velocity.Band.6.Total.Distance, Velocity.Band.7.Total.Distance, Velocity.Band.8.Total.Distance, Maximum.Velocity, Average.Velocity) %>%
  #calculating each player's maximum velocity
  group_by(anon_id) %>%
  mutate(Player.Max.Velocity = max(na.omit(Maximum.Velocity))) %>%
  ungroup() %>%
  #only selecting data from January 1, 2024 and on
  filter(Date >= "2024-01-01")

head(Catapult_Session_clean)
Historical_Running_clean <- Historical_Running %>%
  #taking out rows that don't have data
  filter(Running.Imbalance != "n/a") %>%
  #putting running imbalance as a number and converting the date to a date class
  mutate(Running.Imbalance = as.numeric(Running.Imbalance),
         Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only using data from January 1, 2024 and on
  filter(Date >= "2024-01-01") %>%
  mutate(X=1:4063) %>%
  #making days January 1, 2024
  group_by(anon_id) %>%
  mutate(Days.Since.Start = as.numeric(Date - min(Date))) %>%
  ungroup()

head(Historical_Running_clean)
Incident_Report_clean <- Incident_Report %>%
  #filtering for only hamstring injuries
  filter(OSICS14.Code == "TM1",
         Status != "Full Go")  %>%
  #getting the date of the injury as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y"),
         Date.of.Injury = as.Date(Date.of.Injury...Onset.of.symptoms, "%m/%d/%Y"),
         Examination.Date = as.Date(Examination.Date, "%m/%d/%Y")) %>%
  #only selecting relevant columns for this analysis
  select(anon_id, Position, Date, Date.of.Injury, Time.of.Injury, Side, OSICS.Injury.Diagnosis, Coach.s.Diagnosis, Recurrence.of.Injury, Choose.Season, Onset.of.Symptoms, Injury.Prognosis, General.Mechanism, Specific.Mechanism, Injured.While., Type.of.Event, Season., Status, Days.in.Status) %>%
  group_by(anon_id, Date.of.Injury) %>%
  mutate(Days.Out = sum(Days.in.Status)) %>%
  ungroup()

head(Incident_Report_clean)
#taking the IDs of players who are and aren't injured
all_IDs <- unique(Historical_Running_clean$anon_id)
#taking IDs that were injured and also have running imbalance data
injured_IDs <- intersect(unique(Incident_Report_clean$anon_id), all_IDs)
#taking all players with running imbalance data that don't have an injury
uninjured_IDs <- unique((Historical_Running_clean %>%
  filter(!anon_id %in% injured_IDs))$anon_id)
#injured players who also have running imbalance data
Incident_Report_clean <- Incident_Report_clean %>%
  filter(anon_id %in% injured_IDs)

#all players that only have running imbalance data or have both running imbalance data and incidence report
Historical_Running_clean <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs | anon_id %in% uninjured_IDs)
#removing all_IDs vector and uncleaned data sets
remove(all_IDs)
remove(Incident_Report)
remove(Historical_Running)
remove(Catapult_Session)

Section 1: Running Speed

How often are athletes reaching ≥ 90% maximum velocity throughout a training season?

Should we consider the number of sprinting efforts that athletes are completing?

Are relative efforts and bands more advantageous than the absolute bands provided?

How does sprinting exposure (# of efforts, % max reached) relate to incidence of hamstring injuries?

Section 2: Running Imbalance

What is the variation at the team level and at each individual athlete level?

Each player tends to have a very unique trend in their running imbalance. Looking at how the team varies but also at how each player varies throughout the season, it’s hard to make out any pattern that’s applicable to most people. The variance of running imbalance varies greatly between each player. Instead we looked at what are the variances between players who were injured and those who were not. Based on three different bootstrapped findings, we can see that the variances between players who were injured and those who were not were both statistically significant. For the first bootstrap, we compared the variance of the pooled groups meaning that the variance in running imbalance for players who were injured and those who were not were compared. This resulted in a 90% confidence interval which suggested that the difference in variance between the two groups is between 0.30 and 3.45. This suggests that when looking at the variance of the two groups separately but all the players are pooled together, the variances will most likely be different by factor between 0.30 and 3.45 and the variance for the injured pool will be greater than that of the uninjured pool. For the second bootstrap, each player’s variance was taken individually. This unpooled approach was taken to see if a player’s variance in running imbalance could potentially be related to HSI risk. The bootstrap algorithm in this case took the averaged variances of the bootstrapped sample for each group and compared them. This bootstrap produced a 90% confidence interval for the difference in average variance between the two groups is 0.79 to 1.32. These results suggest that players who sustained a hamstring injury since January 1, 2024 had, on average, a greater variance in their running imbalance by about 1.07. This suggests that there is a relationship between variance in running imbalance and HSI risk. This found increase in variability will be used to address the following questions. For the third bootstrap, we wanted to see if there was a difference in the average mean absolute value in running imbalance between players who were and weren’t injured. The bootstrapping algorithm for this test calculated the average absolute distance value or each running imbalance measurement and found the average for each sample. This test found that at the 90% significance level, injured players had an average running imbalance absolute value between 0.06 and 0.32 greater than their uninjured counterparts. These values though, when we consider that the range of running imbalance goes from 0 to 100 is small and may be hard to detect when out in the field.

#team variation
Historical_Running_clean %>%
  summarize(Team_Variation = var(Running.Imbalance))

#individual player variation
Historical_Running_clean %>%
  group_by(anon_id) %>%
  summarize(Player_Variation = var(na.omit(Running.Imbalance))) %>%
  ungroup() 
#calculating mean and variance for team data
team_mean <- mean(Historical_Running_clean$Running.Imbalance)
team_sd <- sd(Historical_Running_clean$Running.Imbalance)

#making scatter plot of team running imbalance data throughout season
ggplot(Historical_Running_clean, aes(Date, Running.Imbalance)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = team_mean, color = "#CFB87C") +
  geom_hline(yintercept = team_mean + team_sd) +
  geom_hline(yintercept = team_mean - team_sd) +
  geom_hline(yintercept = team_mean + (2*team_sd), color = "#A2A4A3") +
  geom_hline(yintercept = team_mean - (2*team_sd), color = "#A2A4A3") +
  labs(title = "Team Running Imbalance Since January 1, 2024", y="Running Imbalance (%)",
       subtitle = "\u03BC = 0.08623412, \u03C3^2 = 14.94215")

#making histogram of team running imbalance data
ggplot(Historical_Running_clean, aes(Running.Imbalance)) +
  geom_histogram() +
  labs(title = "Team Running Imbalance Since January 1, 2024", x="Running Imbalance")
#making histogram for running imbalance of all injured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.025), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.975), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#making histogram for running imbalance of all uninjured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "black", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.025)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.975)) +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#Plotting injured and uninjured histograms over top one another
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(alpha = 0.75) +
  #adding in 95% CI for uninjured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.025)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.975)) +
  geom_histogram(data = Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance), fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% CI for injured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.025), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.975), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with and without HSI since January 1, 2024")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#making scatter plot of running imbalance data for injured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024")
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_point()`).

#making scatter plot of running imbalance for uninjured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024")
Warning: Removed 1 row containing missing values or values outside the scale range (`geom_point()`).

Testing to see if significantly different

#Splitting up the data sets into injured and uninjured groups
injured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()

uninjured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()
#making a data frame to hold all of the within group variances
group_variances <- data.frame(injured_var = rep(NA,1000),
                uninjured_var = rep(NA,1000),
                diff_in_var = rep(NA, 1000))

#bootstrap for variances, 1000 iterations
for(i in 1:1000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1672)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2391)
  
  #storing the calculated variances in data frame
  group_variances[i,1] = var(injured_sample$Running.Imbalance)
  group_variances[i,2] = var(uninjured_sample$Running.Imbalance)
  group_variances[i,3] = group_variances[i,1] - group_variances[i,2]
}
ggplot(data=group_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.95), color= "#CFB87C")

ggplot(data=group_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75)
#making a data frame to hold all of the average player variances between groups
mean_player_variances <- data.frame(injured_var = rep(NA,1000),
                uninjured_var = rep(NA,1000),
                diff_in_var = rep(NA, 1000))

for(i in 1:1000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1672)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2391)
  
  #storing the calculated variances in data frame
  mean_player_variances[i,1] = mean(na.omit(injured_sample$Player.Variance))
  mean_player_variances[i,2] = mean(na.omit(uninjured_sample$Player.Variance))
  mean_player_variances[i,3] = mean_player_variances[i,1] - mean_player_variances[i,2]
}
ggplot(data = mean_player_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(x="Difference in Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=mean_player_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(x="Average Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#making a data frame to hold all of the average absolute differences from 0
group_distance <- data.frame(injured_dist = rep(NA,1000),
                uninjured_dist = rep(NA,1000),
                diff_in_dist = rep(NA, 1000))

#bootstrap for variances, 1000 iterations
for(i in 1:1000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1658)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2405)
  
  #storing the calculated variances in data frame
  group_distance[i,1] = mean(injured_sample$Player.Absolute.Dist)
  group_distance[i,2] = mean(uninjured_sample$Player.Absolute.Dist)
  group_distance[i,3] = group_distance[i,1] - group_distance[i,2]
}
ggplot(data = group_distance, aes(diff_in_dist)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.95), color= "#CFB87C") +
  labs(x="Difference in Distance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = group_distance) +
  geom_histogram(aes(injured_dist), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_dist), alpha = 0.75) +
  labs(x="Average Distance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#removing junk that came from the loops
remove(i,team_mean, team_sd, injured_sample, uninjured_sample, group_variances, injured_data, uninjured_data, mean_player_variances, group_distance)

What is a meaningful change? What red flags should go off when we see a week-to-week change in running imbalance?

Based on the analysis below, there doesn’t seem to be any major discernible differences in running imbalance before or following an injury. This suggests that there may not be a direct link between HSI risk and running imbalance directly beforehand. Instead, the trends of many players who were injured seems to have a relatively consistent or trend not entirely dependent on time. Looking at summary statistics of running imbalance in the weeks leading up to and following a hamstring injury, there are also no glaring trends. For this analysis, we looked at the mean and variance in running imbalance per week leading up to and after an injury for all of the injured players with running imbalance data. This showed us that there is no clear indicator of HSI risk in running imbalance or any summary statistic of it. Instead it may be more useful to look at each player’s total running imbalance and their individual variance. This seems to be more of a useful tool for differentiating between injured and uninjured athletes.

#getting running imbalances for just injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#making new column to represent when in time injury would be, negative means before injury and positive means after injury
Injured_Historical_Running$Weeks.After.Injury <- rep(NA, 1658)
Injured_Historical_Running$Injury.Count <- rep(NA, 1658)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  for(j in 1:length(injury_dates)){
    #calculate dates for 1, 2, 3, 4 weeks before and after each injury date
    past_1 <- injury_dates[j]-7
    past_2 <- injury_dates[j]-14
    past_3 <- injury_dates[j]-21
    past_4 <- injury_dates[j]-28
    future_1 <- injury_dates[j]+7
    future_2 <- injury_dates[j]+14
    future_3 <- injury_dates[j]+21
    future_4 <- injury_dates[j]+28
    
    #Calculating how many injuries this is
    injury_count <- as.character((length(injury_dates)) - j + 1)
    
    #compare date for each player to date of injury, store in Weeks.After.Injury column, store injury count
    
    #first week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Weeks.After.Injury <- "1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Injury.Count <- injury_count
    
    #second week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Weeks.After.Injury <- "2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Injury.Count <- injury_count
    
    #third week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Weeks.After.Injury <- "3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Injury.Count <- injury_count
    
    #fourth week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Weeks.After.Injury <- "4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Injury.Count <- injury_count    
    #week right before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Weeks.After.Injury <- "-1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Injury.Count <- injury_count
    #two weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Weeks.After.Injury <- "-2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Injury.Count <- injury_count
    
    #three weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Weeks.After.Injury <- "-3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Injury.Count <- injury_count
        
    #four weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Weeks.After.Injury <- "-4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Injury.Count <- injury_count
    
    #Date of Injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Weeks.After.Injury <- "0"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Injury.Count <- injury_count
  }
}


#making weeks after injury and injury count into a factor and combining data sets back together
Injured_Historical_Running <- Injured_Historical_Running %>%
  mutate(Weeks.After.Injury = factor(Weeks.After.Injury),
         Injury.Count = factor(Injury.Count))

Historical_Running_clean <- left_join(Historical_Running_clean, Injured_Historical_Running)

#getting rid of junk that was from the loop
remove(future_1, future_2, future_3, future_4, i, injury_count, injury_dates, j, past_1, past_2, past_3, past_4)
remove(Injured_Historical_Running)
for(i in 1:22){
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id==injured_IDs[i],], aes(Date, Running.Imbalance)) +
    geom_line() +
    geom_point(aes(color=Weeks.After.Injury)) +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title=injured_IDs[i])+
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue")) +
    theme_minimal()
  
  print(p)
}

#making summary statistics of running imbalance per week relative to injury
Historical_Running_clean <- Historical_Running_clean %>%
  group_by(anon_id, Injury.Count, Weeks.After.Injury) %>%
  mutate(Weeks.After.Injury.Variability = var(Running.Imbalance),
         Weeks.After.Injury.Mean = mean(abs(Running.Imbalance))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Mean, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title=injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}
Warning: Removed 17 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 17 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 14 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 14 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 90 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 90 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 70 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 70 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 15 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 15 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 17 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 15 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 15 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 22 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 22 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 29 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 29 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 51 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 51 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 100 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 100 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 101 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 101 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 47 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 47 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 134 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 134 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 46 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 46 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 28 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 28 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 111 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 111 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 131 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 131 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range (`geom_point()`).

for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Variability, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title=injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}
Warning: Removed 18 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 20 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 93 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 97 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 71 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 73 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 18 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 19 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 30 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 31 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 52 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 55 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 100 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 101 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 102 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 104 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 48 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 49 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 134 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 135 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 46 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 46 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 113 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 113 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 131 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 131 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_point()`).

remove(i, p)

Is running imbalance sensitive enough of a metric to use as a prognosis tool versus a rehab tool?

Incident_Report_clean <- Incident_Report_clean %>%
  filter(!is.na(Injury.Prognosis))%>%
  mutate(Expected.Start.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury,
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(7),
                                                      Date.of.Injury+days(28))))),
         Expected.End.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury+days(7),
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(28),
                                                      Date.of.Injury+days(56)))))) %>%
  group_by(anon_id, Date.of.Injury) %>%
  mutate(Actual.Return = Date.of.Injury+days(sum(Days.in.Status))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Running.Imbalance, group=Injury.Count)) +
  geom_line(linewidth=0.1) +
  geom_point() +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury, color="red") +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.Start.Return, color="purple", linetype=3) +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return, color="purple", linetype=3) +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return, color="purple", linetype=1) +
    labs(title=injured_IDs[i])
  
  print(p)
}

LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCiNBdXRob3JzOiBbSU5TRVJUIE5BTUVTIEhFUkVdDQojQXV0aG9yIERhdGU6IA0KI1B1cnBvc2U6IFRoZSBwdXJwb3NlIG9mIHRoaXMgbm90ZWJvb2sgaXMgdG8gaG91c2UgYWxsIGRhdGEgc2V0IHRyYW5zZm9ybWF0aW9uLCBjbGVhbnNpbmcsIHZpc3VhbGl6YXRpb24sIHN0YXRpc3RpY2FsIGFuYWx5c2lzLCBhbmQgbm90ZS10YWtpbmcgZm9yIHRoZSAyMDI1IENVIEF0aGxldGljIERlcGFydG1lbnQgU3BvcnRzIFNjaWVuY2UgSW50ZXJuc2hpcCBQcm9ncmFtDQoNCiNMQVNUIFVQREFURUQ6IA0KDQojSW5jbHVkaW5nIGhlbHBmdWwgbGlicmFyaWVzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShhb2QpDQpgYGANCg0KDQojIERhdGEgQ2xlYW5pbmcNCmBgYHtyIExvYWRpbmcgaW4gdGhlIGRhdGEgc2V0c30NCiNsb2FkaW5nIGluIHRoZSBDYXRhcHVsdCBkYXRhIHRvIGxvb2sgYXQgc3ByaW50aW5nIHZhbHVlcw0KQ2F0YXB1bHRfU2Vzc2lvbiA8LSByZWFkX2NzdigiZGF0YS1zZXRzL2RhdGEtc2V0cy11bmNvbXByZXNzZWQvZGF0YS1zZXRzLWNvbXByZXNzZWQvUnVubmluZyBJbWJhbGFuY2UgYW5kIFNwZWVkL0NhdGFwdWx0IFNlc3Npb24gLSBPdXRkb29yIEZCLmNzdiIpDQoNCiNsb2FkaW5nIGluIHRoZSBIaXN0b3JpY2FsIFJ1bm5pbmcgZGF0YSB0byBsb29rIGF0IHJ1bm5pbmcgaW1iYWxhbmNlIHZhbHVlcw0KSGlzdG9yaWNhbF9SdW5uaW5nIDwtIHJlYWRfY3N2KCJkYXRhLXNldHMvZGF0YS1zZXRzLXVuY29tcHJlc3NlZC9kYXRhLXNldHMtY29tcHJlc3NlZC9SdW5uaW5nIEltYmFsYW5jZSBhbmQgU3BlZWQvQ29tcGlsZWQgSGlzdG9yaWNhbCBSdW5uaW5nIEltYmFsYW5jZSBGQi5jc3YiKQ0KDQojbG9hZGluZyBpbiB0aGUgSW5jaWRlbnQgUmVwb3J0IHRvIGxvb2sgYXQgSFNJcw0KSW5jaWRlbnRfUmVwb3J0IDwtIHJlYWRfY3N2KCJkYXRhLXNldHMvZGF0YS1zZXRzLXVuY29tcHJlc3NlZC9kYXRhLXNldHMtY29tcHJlc3NlZC9SdW5uaW5nIEltYmFsYW5jZSBhbmQgU3BlZWQvSW5jaWRlbnQgUmVwb3J0IEZCIElEcy5jc3YiKQ0KDQpgYGANCg0KYGBge3IgQ2xlYW5pbmcgQ2F0YXB1bHRfU2Vzc2lvbn0NCkNhdGFwdWx0X1Nlc3Npb25fY2xlYW4gPC0gQ2F0YXB1bHRfU2Vzc2lvbiAlPiUNCiAgI3B1dHRpbmcgdGhlIGRhdGUgYXMgYSBkYXRlIGNsYXNzDQogIG11dGF0ZShEYXRlID0gYXMuRGF0ZShEYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHNlbGVjdGluZyBpbXBvcnRhbnQgY29sdW1ucyBmb3IgdGhpcyBhbmFseXNpcw0KICBzZWxlY3QoYW5vbl9pZCwgRGF0ZSwgQWdlLCBQcmltYXJ5LlBvc2l0aW9uLCBUb3RhbC5EaXN0YW5jZSwgUGVyaW9kLk5hbWUsIFRvdGFsLkR1cmF0aW9uLi5taW4uLCBWZWxvY2l0eS5CYW5kLjEuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuMi5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC4zLlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjQuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuNS5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC42LlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjcuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuOC5Ub3RhbC5EaXN0YW5jZSwgTWF4aW11bS5WZWxvY2l0eSwgQXZlcmFnZS5WZWxvY2l0eSkgJT4lDQogICNjYWxjdWxhdGluZyBlYWNoIHBsYXllcidzIG1heGltdW0gdmVsb2NpdHkNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuTWF4LlZlbG9jaXR5ID0gbWF4KG5hLm9taXQoTWF4aW11bS5WZWxvY2l0eSkpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICAjb25seSBzZWxlY3RpbmcgZGF0YSBmcm9tIEphbnVhcnkgMSwgMjAyNCBhbmQgb24NCiAgZmlsdGVyKERhdGUgPj0gIjIwMjQtMDEtMDEiKQ0KDQpoZWFkKENhdGFwdWx0X1Nlc3Npb25fY2xlYW4pDQpgYGANCg0KYGBge3IgQ2xlYW5pbmcgSGlzdG9yaWNhbF9SdW5uaW5nfQ0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZyAlPiUNCiAgI3Rha2luZyBvdXQgcm93cyB0aGF0IGRvbid0IGhhdmUgZGF0YQ0KICBmaWx0ZXIoUnVubmluZy5JbWJhbGFuY2UgIT0gIm4vYSIpICU+JQ0KICAjcHV0dGluZyBydW5uaW5nIGltYmFsYW5jZSBhcyBhIG51bWJlciBhbmQgY29udmVydGluZyB0aGUgZGF0ZSB0byBhIGRhdGUgY2xhc3MNCiAgbXV0YXRlKFJ1bm5pbmcuSW1iYWxhbmNlID0gYXMubnVtZXJpYyhSdW5uaW5nLkltYmFsYW5jZSksDQogICAgICAgICBEYXRlID0gYXMuRGF0ZShEYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHVzaW5nIGRhdGEgZnJvbSBKYW51YXJ5IDEsIDIwMjQgYW5kIG9uDQogIGZpbHRlcihEYXRlID49ICIyMDI0LTAxLTAxIikgJT4lDQogIG11dGF0ZShYPTE6NDA2MykgJT4lDQogICNtYWtpbmcgZGF5cyBKYW51YXJ5IDEsIDIwMjQNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShEYXlzLlNpbmNlLlN0YXJ0ID0gYXMubnVtZXJpYyhEYXRlIC0gbWluKERhdGUpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpoZWFkKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbikNCmBgYA0KDQpgYGB7ciBDbGVhbmluZyBJbmNpZGVudF9SZXBvcnR9DQpJbmNpZGVudF9SZXBvcnRfY2xlYW4gPC0gSW5jaWRlbnRfUmVwb3J0ICU+JQ0KICAjZmlsdGVyaW5nIGZvciBvbmx5IGhhbXN0cmluZyBpbmp1cmllcw0KICBmaWx0ZXIoT1NJQ1MxNC5Db2RlID09ICJUTTEiLA0KICAgICAgICAgU3RhdHVzICE9ICJGdWxsIEdvIikgICU+JQ0KICAjZ2V0dGluZyB0aGUgZGF0ZSBvZiB0aGUgaW5qdXJ5IGFzIGEgZGF0ZSBjbGFzcw0KICBtdXRhdGUoRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgIiVtLyVkLyVZIiksDQogICAgICAgICBEYXRlLm9mLkluanVyeSA9IGFzLkRhdGUoRGF0ZS5vZi5Jbmp1cnkuLi5PbnNldC5vZi5zeW1wdG9tcywgIiVtLyVkLyVZIiksDQogICAgICAgICBFeGFtaW5hdGlvbi5EYXRlID0gYXMuRGF0ZShFeGFtaW5hdGlvbi5EYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHNlbGVjdGluZyByZWxldmFudCBjb2x1bW5zIGZvciB0aGlzIGFuYWx5c2lzDQogIHNlbGVjdChhbm9uX2lkLCBQb3NpdGlvbiwgRGF0ZSwgRGF0ZS5vZi5Jbmp1cnksIFRpbWUub2YuSW5qdXJ5LCBTaWRlLCBPU0lDUy5Jbmp1cnkuRGlhZ25vc2lzLCBDb2FjaC5zLkRpYWdub3NpcywgUmVjdXJyZW5jZS5vZi5Jbmp1cnksIENob29zZS5TZWFzb24sIE9uc2V0Lm9mLlN5bXB0b21zLCBJbmp1cnkuUHJvZ25vc2lzLCBHZW5lcmFsLk1lY2hhbmlzbSwgU3BlY2lmaWMuTWVjaGFuaXNtLCBJbmp1cmVkLldoaWxlLiwgVHlwZS5vZi5FdmVudCwgU2Vhc29uLiwgU3RhdHVzLCBEYXlzLmluLlN0YXR1cykgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIERhdGUub2YuSW5qdXJ5KSAlPiUNCiAgbXV0YXRlKERheXMuT3V0ID0gc3VtKERheXMuaW4uU3RhdHVzKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpoZWFkKEluY2lkZW50X1JlcG9ydF9jbGVhbikNCmBgYA0KDQpgYGB7ciBJZGVudGlmeWluZyBwbGF5ZXJzIHRoYXQgaGF2ZSBkYXRhIGluIGJvdGggZGF0YSBzZXRzfQ0KI3Rha2luZyB0aGUgSURzIG9mIHBsYXllcnMgd2hvIGFyZSBhbmQgYXJlbid0IGluanVyZWQNCmFsbF9JRHMgPC0gdW5pcXVlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkKQ0KI3Rha2luZyBJRHMgdGhhdCB3ZXJlIGluanVyZWQgYW5kIGFsc28gaGF2ZSBydW5uaW5nIGltYmFsYW5jZSBkYXRhDQppbmp1cmVkX0lEcyA8LSBpbnRlcnNlY3QodW5pcXVlKEluY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkKSwgYWxsX0lEcykNCiN0YWtpbmcgYWxsIHBsYXllcnMgd2l0aCBydW5uaW5nIGltYmFsYW5jZSBkYXRhIHRoYXQgZG9uJ3QgaGF2ZSBhbiBpbmp1cnkNCnVuaW5qdXJlZF9JRHMgPC0gdW5pcXVlKChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcighYW5vbl9pZCAlaW4lIGluanVyZWRfSURzKSkkYW5vbl9pZCkNCmBgYA0KDQpgYGB7ciBGaWx0ZXJpbmcgb3V0IHBsYXllcnMgd2l0aG91dCBwcm9wZXIgZGF0YSBmcm9tIGFsbCBkYXRhIHNldHN9DQojaW5qdXJlZCBwbGF5ZXJzIHdobyBhbHNvIGhhdmUgcnVubmluZyBpbWJhbGFuY2UgZGF0YQ0KSW5jaWRlbnRfUmVwb3J0X2NsZWFuIDwtIEluY2lkZW50X1JlcG9ydF9jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBpbmp1cmVkX0lEcykNCg0KI2FsbCBwbGF5ZXJzIHRoYXQgb25seSBoYXZlIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgb3IgaGF2ZSBib3RoIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgYW5kIGluY2lkZW5jZSByZXBvcnQNCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMgfCBhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcykNCmBgYA0KDQpgYGB7ciBSZW1vdmluZyBhbGwgdW5pbXBvcnRhbnQgb2JqZWN0cyBjcmVhdGVkIGR1cmluZyBjbGVhbmluZ30NCiNyZW1vdmluZyBhbGxfSURzIHZlY3RvciBhbmQgdW5jbGVhbmVkIGRhdGEgc2V0cw0KcmVtb3ZlKGFsbF9JRHMpDQpyZW1vdmUoSW5jaWRlbnRfUmVwb3J0KQ0KcmVtb3ZlKEhpc3RvcmljYWxfUnVubmluZykNCnJlbW92ZShDYXRhcHVsdF9TZXNzaW9uKQ0KYGBgDQoNCiMgU2VjdGlvbiAxOiBSdW5uaW5nIFNwZWVkDQoNCiMjIEhvdyBvZnRlbiBhcmUgYXRobGV0ZXMgcmVhY2hpbmcg4omlIDkwJSBtYXhpbXVtIHZlbG9jaXR5IHRocm91Z2hvdXQgYSB0cmFpbmluZyBzZWFzb24/DQoNCiMjIFNob3VsZCB3ZSBjb25zaWRlciB0aGUgbnVtYmVyIG9mIHNwcmludGluZyBlZmZvcnRzIHRoYXQgYXRobGV0ZXMgYXJlIGNvbXBsZXRpbmc/DQoNCiMjIEFyZSByZWxhdGl2ZSBlZmZvcnRzIGFuZCBiYW5kcyBtb3JlIGFkdmFudGFnZW91cyB0aGFuIHRoZSBhYnNvbHV0ZSBiYW5kcyBwcm92aWRlZD8NCg0KIyMgSG93IGRvZXMgc3ByaW50aW5nIGV4cG9zdXJlICgjIG9mIGVmZm9ydHMsICUgbWF4IHJlYWNoZWQpIHJlbGF0ZSB0byBpbmNpZGVuY2Ugb2YgaGFtc3RyaW5nIGluanVyaWVzPw0KDQoNCg0KIyBTZWN0aW9uIDI6IFJ1bm5pbmcgSW1iYWxhbmNlDQoNCiMjIFdoYXQgaXMgdGhlIHZhcmlhdGlvbiBhdCB0aGUgdGVhbSBsZXZlbCBhbmQgYXQgZWFjaCBpbmRpdmlkdWFsIGF0aGxldGUgbGV2ZWw/DQoNCiAgRWFjaCBwbGF5ZXIgdGVuZHMgdG8gaGF2ZSBhIHZlcnkgdW5pcXVlIHRyZW5kIGluIHRoZWlyIHJ1bm5pbmcgaW1iYWxhbmNlLiBMb29raW5nIGF0IGhvdyB0aGUgdGVhbSB2YXJpZXMgYnV0IGFsc28gYXQgaG93IGVhY2ggcGxheWVyIHZhcmllcyB0aHJvdWdob3V0IHRoZSBzZWFzb24sIGl0J3MgaGFyZCB0byBtYWtlIG91dCBhbnkgcGF0dGVybiB0aGF0J3MgYXBwbGljYWJsZSB0byBtb3N0IHBlb3BsZS4gVGhlIHZhcmlhbmNlIG9mIHJ1bm5pbmcgaW1iYWxhbmNlIHZhcmllcyBncmVhdGx5IGJldHdlZW4gZWFjaCBwbGF5ZXIuIEluc3RlYWQgd2UgbG9va2VkIGF0IHdoYXQgYXJlIHRoZSB2YXJpYW5jZXMgYmV0d2VlbiBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgYW5kIHRob3NlIHdobyB3ZXJlIG5vdC4gQmFzZWQgb24gdGhyZWUgZGlmZmVyZW50IGJvb3RzdHJhcHBlZCBmaW5kaW5ncywgd2UgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYW5jZXMgYmV0d2VlbiBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgYW5kIHRob3NlIHdobyB3ZXJlIG5vdCB3ZXJlIGJvdGggc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4NCiAgRm9yIHRoZSBmaXJzdCBib290c3RyYXAsIHdlIGNvbXBhcmVkIHRoZSB2YXJpYW5jZSBvZiB0aGUgcG9vbGVkIGdyb3VwcyBtZWFuaW5nIHRoYXQgdGhlIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZvciBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgYW5kIHRob3NlIHdobyB3ZXJlIG5vdCB3ZXJlIGNvbXBhcmVkLiBUaGlzIHJlc3VsdGVkIGluIGEgOTAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgd2hpY2ggc3VnZ2VzdGVkIHRoYXQgdGhlIGRpZmZlcmVuY2UgaW4gdmFyaWFuY2UgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcyBpcyBiZXR3ZWVuIDAuMzAgYW5kIDMuNDUuIFRoaXMgc3VnZ2VzdHMgdGhhdCB3aGVuIGxvb2tpbmcgYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSB0d28gZ3JvdXBzIHNlcGFyYXRlbHkgYnV0IGFsbCB0aGUgcGxheWVycyBhcmUgcG9vbGVkIHRvZ2V0aGVyLCB0aGUgdmFyaWFuY2VzIHdpbGwgbW9zdCBsaWtlbHkgYmUgZGlmZmVyZW50IGJ5IGZhY3RvciBiZXR3ZWVuIDAuMzAgYW5kIDMuNDUgYW5kIHRoZSB2YXJpYW5jZSBmb3IgdGhlIGluanVyZWQgcG9vbCB3aWxsIGJlIGdyZWF0ZXIgdGhhbiB0aGF0IG9mIHRoZSB1bmluanVyZWQgcG9vbC4NCiAgRm9yIHRoZSBzZWNvbmQgYm9vdHN0cmFwLCBlYWNoIHBsYXllcidzIHZhcmlhbmNlIHdhcyB0YWtlbiBpbmRpdmlkdWFsbHkuIFRoaXMgdW5wb29sZWQgYXBwcm9hY2ggd2FzIHRha2VuIHRvIHNlZSBpZiBhIHBsYXllcidzIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGNvdWxkIHBvdGVudGlhbGx5IGJlIHJlbGF0ZWQgdG8gSFNJIHJpc2suIFRoZSBib290c3RyYXAgYWxnb3JpdGhtIGluIHRoaXMgY2FzZSB0b29rIHRoZSBhdmVyYWdlZCB2YXJpYW5jZXMgb2YgdGhlIGJvb3RzdHJhcHBlZCBzYW1wbGUgZm9yIGVhY2ggZ3JvdXAgYW5kIGNvbXBhcmVkIHRoZW0uIFRoaXMgYm9vdHN0cmFwIHByb2R1Y2VkIGEgOTAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBkaWZmZXJlbmNlIGluIGF2ZXJhZ2UgdmFyaWFuY2UgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcyBpcyAwLjc5IHRvIDEuMzIuIFRoZXNlIHJlc3VsdHMgc3VnZ2VzdCB0aGF0IHBsYXllcnMgd2hvIHN1c3RhaW5lZCBhIGhhbXN0cmluZyBpbmp1cnkgc2luY2UgSmFudWFyeSAxLCAyMDI0IGhhZCwgb24gYXZlcmFnZSwgYSBncmVhdGVyIHZhcmlhbmNlIGluIHRoZWlyIHJ1bm5pbmcgaW1iYWxhbmNlIGJ5IGFib3V0IDEuMDcuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGFuZCBIU0kgcmlzay4gVGhpcyBmb3VuZCBpbmNyZWFzZSBpbiB2YXJpYWJpbGl0eSB3aWxsIGJlIHVzZWQgdG8gYWRkcmVzcyB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucy4NCiAgRm9yIHRoZSB0aGlyZCBib290c3RyYXAsIHdlIHdhbnRlZCB0byBzZWUgaWYgdGhlcmUgd2FzIGEgZGlmZmVyZW5jZSBpbiB0aGUgYXZlcmFnZSBtZWFuIGFic29sdXRlIHZhbHVlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGJldHdlZW4gcGxheWVycyB3aG8gd2VyZSBhbmQgd2VyZW4ndCBpbmp1cmVkLiBUaGUgYm9vdHN0cmFwcGluZyBhbGdvcml0aG0gZm9yIHRoaXMgdGVzdCBjYWxjdWxhdGVkIHRoZSBhdmVyYWdlIGFic29sdXRlIGRpc3RhbmNlIHZhbHVlIG9yIGVhY2ggcnVubmluZyBpbWJhbGFuY2UgbWVhc3VyZW1lbnQgYW5kIGZvdW5kIHRoZSBhdmVyYWdlIGZvciBlYWNoIHNhbXBsZS4gVGhpcyB0ZXN0IGZvdW5kIHRoYXQgYXQgdGhlIDkwJSBzaWduaWZpY2FuY2UgbGV2ZWwsIGluanVyZWQgcGxheWVycyBoYWQgYW4gYXZlcmFnZSBydW5uaW5nIGltYmFsYW5jZSBhYnNvbHV0ZSB2YWx1ZSBiZXR3ZWVuIDAuMDYgYW5kIDAuMzIgZ3JlYXRlciB0aGFuIHRoZWlyIHVuaW5qdXJlZCBjb3VudGVycGFydHMuIFRoZXNlIHZhbHVlcyB0aG91Z2gsIHdoZW4gd2UgY29uc2lkZXIgdGhhdCB0aGUgcmFuZ2Ugb2YgcnVubmluZyBpbWJhbGFuY2UgZ29lcyBmcm9tIDAgdG8gMTAwIGlzIHNtYWxsIGFuZCBtYXkgYmUgaGFyZCB0byBkZXRlY3Qgd2hlbiBvdXQgaW4gdGhlIGZpZWxkLiANCg0KYGBge3IgTG9va2luZyBhdCB0ZWFtIGFuZCBwbGF5ZXIgdmFyaWFuY2VzIG9mIHJ1bm5pbmcgaW1iYWxhbmNlfQ0KI3RlYW0gdmFyaWF0aW9uDQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIHN1bW1hcml6ZShUZWFtX1ZhcmlhdGlvbiA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpDQoNCiNpbmRpdmlkdWFsIHBsYXllciB2YXJpYXRpb24NCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIHN1bW1hcml6ZShQbGF5ZXJfVmFyaWF0aW9uID0gdmFyKG5hLm9taXQoUnVubmluZy5JbWJhbGFuY2UpKSkgJT4lDQogIHVuZ3JvdXAoKSANCmBgYA0KDQoNCmBgYHtyIFJ1bm5pbmcgaW1iYWxhbmNlIG1lYXN1cmVtZW50cyBmb3Igd2hvbGUgdGVhbSBzaW5jZSBKYW51YXJ5IDEsIDIwMjR9DQojY2FsY3VsYXRpbmcgbWVhbiBhbmQgdmFyaWFuY2UgZm9yIHRlYW0gZGF0YQ0KdGVhbV9tZWFuIDwtIG1lYW4oSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJFJ1bm5pbmcuSW1iYWxhbmNlKQ0KdGVhbV9zZCA8LSBzZChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kUnVubmluZy5JbWJhbGFuY2UpDQoNCiNtYWtpbmcgc2NhdHRlciBwbG90IG9mIHRlYW0gcnVubmluZyBpbWJhbGFuY2UgZGF0YSB0aHJvdWdob3V0IHNlYXNvbg0KZ2dwbG90KEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuICsgdGVhbV9zZCkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4gLSB0ZWFtX3NkKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiArICgyKnRlYW1fc2QpLCBjb2xvciA9ICIjQTJBNEEzIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4gLSAoMip0ZWFtX3NkKSwgY29sb3IgPSAiI0EyQTRBMyIpICsNCiAgbGFicyh0aXRsZSA9ICJUZWFtIFJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCIsIHk9IlJ1bm5pbmcgSW1iYWxhbmNlICglKSIsDQogICAgICAgc3VidGl0bGUgPSAiXHUwM0JDID0gMC4wODYyMzQxMiwgXHUwM0MzXjIgPSAxNC45NDIxNSIpDQoNCiNtYWtpbmcgaGlzdG9ncmFtIG9mIHRlYW0gcnVubmluZyBpbWJhbGFuY2UgZGF0YQ0KZ2dwbG90KEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiwgYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgbGFicyh0aXRsZSA9ICJUZWFtIFJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCIsIHg9IlJ1bm5pbmcgSW1iYWxhbmNlIikNCmBgYA0KDQoNCmBgYHtyIExvb2tpbmcgYXQgZGlzdHJpYnV0aW9ucyBmb3IgaW5qdXJlZCBhbmQgdW5pbmp1cmVkIHNlcGFyYXRlbHl9DQojbWFraW5nIGhpc3RvZ3JhbSBmb3IgcnVubmluZyBpbWJhbGFuY2Ugb2YgYWxsIGluanVyZWQgYXRobGV0ZXMNCmdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0sIGFlcyhSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICIjQ0ZCODdDIiwgYWxwaGEgPSAwLjc1KSArDQogICNhZGRpbmcgaW4gOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC4wMjUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjk3NSksIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIHhsaW0oLTIxLDIxKSArDQogIGxhYnModGl0bGUgPSAiUnVubmluZyBJbWJhbGFuY2UgZm9yIFBsYXllcnMgd2l0aCBIU0kgc2luY2UgSmFudWFyeSAxLCAyMDI0IikNCg0KI21ha2luZyBoaXN0b2dyYW0gZm9yIHJ1bm5pbmcgaW1iYWxhbmNlIG9mIGFsbCB1bmluanVyZWQgYXRobGV0ZXMNCmdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSwgYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gImJsYWNrIiwgYWxwaGEgPSAwLjc1KSArDQogICNhZGRpbmcgaW4gOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjAyNSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjk3NSkpICsNCiAgeGxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRob3V0IEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKQ0KDQojUGxvdHRpbmcgaW5qdXJlZCBhbmQgdW5pbmp1cmVkIGhpc3RvZ3JhbXMgb3ZlciB0b3Agb25lIGFub3RoZXINCmdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSwgYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgQ0kgZm9yIHVuaW5qdXJlZCBwbGF5ZXJzDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC4wMjUpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC45NzUpKSArDQogIGdlb21faGlzdG9ncmFtKGRhdGEgPSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpLCBmaWxsID0gIiNDRkI4N0MiLCBhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgQ0kgZm9yIGluanVyZWQgcGxheWVycw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjAyNSksIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuOTc1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgeGxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRoIGFuZCB3aXRob3V0IEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKQ0KYGBgDQoNCg0KYGBge3IgbG9va2luZyBhdCB0cmVuZHMgZm9yIGluanVyZWQgYW5kIHVuaW5qdXJlZCBwbGF5ZXJzIHNpbmNlIDEtMS0yMDI0fQ0KI21ha2luZyBzY2F0dGVyIHBsb3Qgb2YgcnVubmluZyBpbWJhbGFuY2UgZGF0YSBmb3IgaW5qdXJlZCBwbGF5ZXJzDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArIA0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArDQogIHlsaW0oLTIxLDIxKSArDQogIGxhYnModGl0bGUgPSAiUnVubmluZyBJbWJhbGFuY2UgZm9yIFBsYXllcnMgd2l0aCBIU0kgc2luY2UgSmFudWFyeSAxLCAyMDI0IikNCg0KI21ha2luZyBzY2F0dGVyIHBsb3Qgb2YgcnVubmluZyBpbWJhbGFuY2UgZm9yIHVuaW5qdXJlZCBwbGF5ZXJzDQpnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsgDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsNCiAgeWxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRob3V0IEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKQ0KYGBgDQoNCg0KIyMjIFRlc3RpbmcgdG8gc2VlIGlmIHNpZ25pZmljYW50bHkgZGlmZmVyZW50DQpgYGB7ciBTZXBhcmF0aW5nIGluanVyZWQgYW5kIHVuaW5qdXJlZCBpbnRvIHR3byBkaWZmZXJlbnQgZGF0YSBzZXRzfQ0KI1NwbGl0dGluZyB1cCB0aGUgZGF0YSBzZXRzIGludG8gaW5qdXJlZCBhbmQgdW5pbmp1cmVkIGdyb3Vwcw0KaW5qdXJlZF9kYXRhIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0gJT4lDQogIG11dGF0ZShQbGF5ZXIuQWJzb2x1dGUuRGlzdCA9IGFicyhSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKFBsYXllci5WYXJpYW5jZSA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KdW5pbmp1cmVkX2RhdGEgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdICU+JQ0KICBtdXRhdGUoUGxheWVyLkFic29sdXRlLkRpc3QgPSBhYnMoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuVmFyaWFuY2UgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciBCb29zdHJhcHBpbmcgdG8gbG9vayBhdCB2YXJpYW5jZXMgb2YgZGlmZmVyZW50IGdyb3VwcywgcG9vbGVkfQ0KI21ha2luZyBhIGRhdGEgZnJhbWUgdG8gaG9sZCBhbGwgb2YgdGhlIHdpdGhpbiBncm91cCB2YXJpYW5jZXMNCmdyb3VwX3ZhcmlhbmNlcyA8LSBkYXRhLmZyYW1lKGluanVyZWRfdmFyID0gcmVwKE5BLDEwMDApLA0KICAgICAgICAgICAgICAgIHVuaW5qdXJlZF92YXIgPSByZXAoTkEsMTAwMCksDQogICAgICAgICAgICAgICAgZGlmZl9pbl92YXIgPSByZXAoTkEsIDEwMDApKQ0KDQojYm9vdHN0cmFwIGZvciB2YXJpYW5jZXMsIDEwMDAgaXRlcmF0aW9ucw0KZm9yKGkgaW4gMToxMDAwKXsNCiAgI3JhbmRvbSBzZWVkDQogIHNldC5zZWVkKGkpIA0KICANCiAgI3Rha2luZyBzYW1wbGVzIGZyb20gZWFjaCBvZiB0aGUgZGF0YSBzZXRzLCBzYW1lIG51bWJlciBvZiByb3dzLCByZXBsYWNlbWVudCB0cnVlDQogIGluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKGluanVyZWRfZGF0YSwgcmVwbGFjZSA9IFRSVUUsIHNpemUgPSAxNjcyKQ0KICB1bmluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKHVuaW5qdXJlZF9kYXRhLCByZXBsYWNlPVRSVUUsIHNpemUgPSAyMzkxKQ0KICANCiAgI3N0b3JpbmcgdGhlIGNhbGN1bGF0ZWQgdmFyaWFuY2VzIGluIGRhdGEgZnJhbWUNCiAgZ3JvdXBfdmFyaWFuY2VzW2ksMV0gPSB2YXIoaW5qdXJlZF9zYW1wbGUkUnVubmluZy5JbWJhbGFuY2UpDQogIGdyb3VwX3ZhcmlhbmNlc1tpLDJdID0gdmFyKHVuaW5qdXJlZF9zYW1wbGUkUnVubmluZy5JbWJhbGFuY2UpDQogIGdyb3VwX3ZhcmlhbmNlc1tpLDNdID0gZ3JvdXBfdmFyaWFuY2VzW2ksMV0gLSBncm91cF92YXJpYW5jZXNbaSwyXQ0KfQ0KYGBgDQoNCg0KYGBge3IgUGxvdHRpbmcgcmVzdWx0cyBmcm9tIHBvb2xlZCBib29zdHJhcHBlZCB2YXJpYW5jZXN9DQpnZ3Bsb3QoZGF0YT1ncm91cF92YXJpYW5jZXMsIGFlcyhkaWZmX2luX3ZhcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX3ZhcmlhbmNlcyRkaWZmX2luX3ZhciwgMC4wNSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfdmFyaWFuY2VzJGRpZmZfaW5fdmFyLCAwLjk1KSwgY29sb3I9ICIjQ0ZCODdDIikNCg0KZ2dwbG90KGRhdGE9Z3JvdXBfdmFyaWFuY2VzKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhpbmp1cmVkX3ZhciksIGFscGhhID0gMC43NSwgZmlsbCA9IiNDRkI4N0MiKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh1bmluanVyZWRfdmFyKSwgYWxwaGEgPSAwLjc1KQ0KYGBgDQoNCg0KYGBge3IgQm9vdHN0cmFwcGluZyB0byBsb29rIGF0IGF2ZXJhZ2UgcGxheWVyIHZhcmlhbmNlc30NCiNtYWtpbmcgYSBkYXRhIGZyYW1lIHRvIGhvbGQgYWxsIG9mIHRoZSBhdmVyYWdlIHBsYXllciB2YXJpYW5jZXMgYmV0d2VlbiBncm91cHMNCm1lYW5fcGxheWVyX3ZhcmlhbmNlcyA8LSBkYXRhLmZyYW1lKGluanVyZWRfdmFyID0gcmVwKE5BLDEwMDApLA0KICAgICAgICAgICAgICAgIHVuaW5qdXJlZF92YXIgPSByZXAoTkEsMTAwMCksDQogICAgICAgICAgICAgICAgZGlmZl9pbl92YXIgPSByZXAoTkEsIDEwMDApKQ0KDQpmb3IoaSBpbiAxOjEwMDApew0KICAjcmFuZG9tIHNlZWQNCiAgc2V0LnNlZWQoaSkgDQogIA0KICAjdGFraW5nIHNhbXBsZXMgZnJvbSBlYWNoIG9mIHRoZSBkYXRhIHNldHMsIHNhbWUgbnVtYmVyIG9mIHJvd3MsIHJlcGxhY2VtZW50IHRydWUNCiAgaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24oaW5qdXJlZF9kYXRhLCByZXBsYWNlID0gVFJVRSwgc2l6ZSA9IDE2NzIpDQogIHVuaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24odW5pbmp1cmVkX2RhdGEsIHJlcGxhY2U9VFJVRSwgc2l6ZSA9IDIzOTEpDQogIA0KICAjc3RvcmluZyB0aGUgY2FsY3VsYXRlZCB2YXJpYW5jZXMgaW4gZGF0YSBmcmFtZQ0KICBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwxXSA9IG1lYW4obmEub21pdChpbmp1cmVkX3NhbXBsZSRQbGF5ZXIuVmFyaWFuY2UpKQ0KICBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwyXSA9IG1lYW4obmEub21pdCh1bmluanVyZWRfc2FtcGxlJFBsYXllci5WYXJpYW5jZSkpDQogIG1lYW5fcGxheWVyX3ZhcmlhbmNlc1tpLDNdID0gbWVhbl9wbGF5ZXJfdmFyaWFuY2VzW2ksMV0gLSBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwyXQ0KfQ0KYGBgDQoNCg0KYGBge3IgUGxvdHRpbmcgcmVzdWx0cyBmcm9tIGF2ZXJhZ2VkIGJvb3RzdHJhcHBlZCBwbGF5ZXIgdmFyaWFuY2VzfQ0KZ2dwbG90KGRhdGEgPSBtZWFuX3BsYXllcl92YXJpYW5jZXMsIGFlcyhkaWZmX2luX3ZhcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKG1lYW5fcGxheWVyX3ZhcmlhbmNlcyRkaWZmX2luX3ZhciwgMC4wNSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUobWVhbl9wbGF5ZXJfdmFyaWFuY2VzJGRpZmZfaW5fdmFyLCAwLjk1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBsYWJzKHg9IkRpZmZlcmVuY2UgaW4gVmFyaWFuY2UiKQ0KDQpnZ3Bsb3QoZGF0YT1tZWFuX3BsYXllcl92YXJpYW5jZXMpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGluanVyZWRfdmFyKSwgYWxwaGEgPSAwLjc1LCBmaWxsID0iI0NGQjg3QyIpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHVuaW5qdXJlZF92YXIpLCBhbHBoYSA9IDAuNzUpICsNCiAgbGFicyh4PSJBdmVyYWdlIFZhcmlhbmNlIikNCmBgYA0KDQoNCmBgYHtyIEJvb3RzdHJhcHBpbmcgdG8gbG9vayBhdCBhdmVyYWdlIGFic29sdXRlIHZhbHVlIHJ1bm5pbmcgaW1iYWxhbmNlfQ0KI21ha2luZyBhIGRhdGEgZnJhbWUgdG8gaG9sZCBhbGwgb2YgdGhlIGF2ZXJhZ2UgYWJzb2x1dGUgZGlmZmVyZW5jZXMgZnJvbSAwDQpncm91cF9kaXN0YW5jZSA8LSBkYXRhLmZyYW1lKGluanVyZWRfZGlzdCA9IHJlcChOQSwxMDAwKSwNCiAgICAgICAgICAgICAgICB1bmluanVyZWRfZGlzdCA9IHJlcChOQSwxMDAwKSwNCiAgICAgICAgICAgICAgICBkaWZmX2luX2Rpc3QgPSByZXAoTkEsIDEwMDApKQ0KDQojYm9vdHN0cmFwIGZvciB2YXJpYW5jZXMsIDEwMDAgaXRlcmF0aW9ucw0KZm9yKGkgaW4gMToxMDAwKXsNCiAgI3JhbmRvbSBzZWVkDQogIHNldC5zZWVkKGkpIA0KICANCiAgI3Rha2luZyBzYW1wbGVzIGZyb20gZWFjaCBvZiB0aGUgZGF0YSBzZXRzLCBzYW1lIG51bWJlciBvZiByb3dzLCByZXBsYWNlbWVudCB0cnVlDQogIGluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKGluanVyZWRfZGF0YSwgcmVwbGFjZSA9IFRSVUUsIHNpemUgPSAxNjU4KQ0KICB1bmluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKHVuaW5qdXJlZF9kYXRhLCByZXBsYWNlPVRSVUUsIHNpemUgPSAyNDA1KQ0KICANCiAgI3N0b3JpbmcgdGhlIGNhbGN1bGF0ZWQgdmFyaWFuY2VzIGluIGRhdGEgZnJhbWUNCiAgZ3JvdXBfZGlzdGFuY2VbaSwxXSA9IG1lYW4oaW5qdXJlZF9zYW1wbGUkUGxheWVyLkFic29sdXRlLkRpc3QpDQogIGdyb3VwX2Rpc3RhbmNlW2ksMl0gPSBtZWFuKHVuaW5qdXJlZF9zYW1wbGUkUGxheWVyLkFic29sdXRlLkRpc3QpDQogIGdyb3VwX2Rpc3RhbmNlW2ksM10gPSBncm91cF9kaXN0YW5jZVtpLDFdIC0gZ3JvdXBfZGlzdGFuY2VbaSwyXQ0KfQ0KYGBgDQoNCg0KYGBge3IgUGxvdHRpbmcgcmVzdWx0cyBmcm9tIGF2ZXJhZ2VkIGJvb3N0cmFwcGVkIGFic29sdXRlIHZhbHVlIHJ1bm5pbmcgaW1iYWxhbmNlfQ0KZ2dwbG90KGRhdGEgPSBncm91cF9kaXN0YW5jZSwgYWVzKGRpZmZfaW5fZGlzdCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX2Rpc3RhbmNlJGRpZmZfaW5fZGlzdCwgMC4wNSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfZGlzdGFuY2UkZGlmZl9pbl9kaXN0LCAwLjk1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBsYWJzKHg9IkRpZmZlcmVuY2UgaW4gRGlzdGFuY2UiKQ0KDQpnZ3Bsb3QoZGF0YSA9IGdyb3VwX2Rpc3RhbmNlKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhpbmp1cmVkX2Rpc3QpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXModW5pbmp1cmVkX2Rpc3QpLCBhbHBoYSA9IDAuNzUpICsNCiAgbGFicyh4PSJBdmVyYWdlIERpc3RhbmNlIikNCmBgYA0KDQoNCmBgYHtyIFJlbW92aW5nIHVubmVjZXNzYXJ5IG9iamVjdHMgZnJvbSBydW5uaW5nIGltYmFsYW5jZSB2YXJpYW5jZSBhbmFseXNpc30NCiNyZW1vdmluZyBqdW5rIHRoYXQgY2FtZSBmcm9tIHRoZSBsb29wcw0KcmVtb3ZlKGksdGVhbV9tZWFuLCB0ZWFtX3NkLCBpbmp1cmVkX3NhbXBsZSwgdW5pbmp1cmVkX3NhbXBsZSwgZ3JvdXBfdmFyaWFuY2VzLCBpbmp1cmVkX2RhdGEsIHVuaW5qdXJlZF9kYXRhLCBtZWFuX3BsYXllcl92YXJpYW5jZXMsIGdyb3VwX2Rpc3RhbmNlKQ0KYGBgDQoNCg0KIyMgV2hhdCBpcyBhIG1lYW5pbmdmdWwgY2hhbmdlPyBXaGF0IHJlZCBmbGFncyBzaG91bGQgZ28gb2ZmIHdoZW4gd2Ugc2VlIGEgd2Vlay10by13ZWVrIGNoYW5nZSBpbiBydW5uaW5nIGltYmFsYW5jZT8NCg0KICBCYXNlZCBvbiB0aGUgYW5hbHlzaXMgYmVsb3csIHRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhbnkgbWFqb3IgZGlzY2VybmlibGUgZGlmZmVyZW5jZXMgaW4gcnVubmluZyBpbWJhbGFuY2UgYmVmb3JlIG9yIGZvbGxvd2luZyBhbiBpbmp1cnkuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGVyZSBtYXkgbm90IGJlIGEgZGlyZWN0IGxpbmsgYmV0d2VlbiBIU0kgcmlzayBhbmQgcnVubmluZyBpbWJhbGFuY2UgZGlyZWN0bHkgYmVmb3JlaGFuZC4gSW5zdGVhZCwgdGhlIHRyZW5kcyBvZiBtYW55IHBsYXllcnMgd2hvIHdlcmUgaW5qdXJlZCBzZWVtcyB0byBoYXZlIGEgcmVsYXRpdmVseSBjb25zaXN0ZW50IG9yIHRyZW5kIG5vdCBlbnRpcmVseSBkZXBlbmRlbnQgb24gdGltZS4gDQogIExvb2tpbmcgYXQgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHJ1bm5pbmcgaW1iYWxhbmNlIGluIHRoZSB3ZWVrcyBsZWFkaW5nIHVwIHRvIGFuZCBmb2xsb3dpbmcgYSBoYW1zdHJpbmcgaW5qdXJ5LCB0aGVyZSBhcmUgYWxzbyBubyBnbGFyaW5nIHRyZW5kcy4gRm9yIHRoaXMgYW5hbHlzaXMsIHdlIGxvb2tlZCBhdCB0aGUgbWVhbiBhbmQgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgbGVhZGluZyB1cCB0byBhbmQgYWZ0ZXIgYW4gaW5qdXJ5IGZvciBhbGwgb2YgdGhlIGluanVyZWQgcGxheWVycyB3aXRoIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEuIFRoaXMgc2hvd2VkIHVzIHRoYXQgdGhlcmUgaXMgbm8gY2xlYXIgaW5kaWNhdG9yIG9mIEhTSSByaXNrIGluIHJ1bm5pbmcgaW1iYWxhbmNlIG9yIGFueSBzdW1tYXJ5IHN0YXRpc3RpYyBvZiBpdC4gSW5zdGVhZCBpdCBtYXkgYmUgbW9yZSB1c2VmdWwgdG8gbG9vayBhdCBlYWNoIHBsYXllcidzIHRvdGFsIHJ1bm5pbmcgaW1iYWxhbmNlIGFuZCB0aGVpciBpbmRpdmlkdWFsIHZhcmlhbmNlLiBUaGlzIHNlZW1zIHRvIGJlIG1vcmUgb2YgYSB1c2VmdWwgdG9vbCBmb3IgZGlmZmVyZW50aWF0aW5nIGJldHdlZW4gaW5qdXJlZCBhbmQgdW5pbmp1cmVkIGF0aGxldGVzLg0KDQpgYGB7ciBDYWxjdWxhdGluZyB3ZWVrcyBiZWZvcmUgYW5kIGFmdGVyIGluanVyeSBvY2N1cmFuY2UgYmFzZWQgb24gZGF0ZSwgaG93IG1hbnkgaW5qdXJpZXMgcGxheWVyIGhhc30NCiNnZXR0aW5nIHJ1bm5pbmcgaW1iYWxhbmNlcyBmb3IganVzdCBpbmp1cmVkIHBsYXllcnMNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBpbmp1cmVkX0lEcykNCg0KI21ha2luZyBuZXcgY29sdW1uIHRvIHJlcHJlc2VudCB3aGVuIGluIHRpbWUgaW5qdXJ5IHdvdWxkIGJlLCBuZWdhdGl2ZSBtZWFucyBiZWZvcmUgaW5qdXJ5IGFuZCBwb3NpdGl2ZSBtZWFucyBhZnRlciBpbmp1cnkNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJFdlZWtzLkFmdGVyLkluanVyeSA8LSByZXAoTkEsIDE2NTgpDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRJbmp1cnkuQ291bnQgPC0gcmVwKE5BLCAxNjU4KQ0KDQojZ28gdGhyb3VnaCBhbGwgb2YgdGhlIGluanVyZWQgcGxheWVycyBpbiB0aGUgZGF0YSBzZXQNCmZvcihpIGluIDE6MjIpew0KICAjZ2V0IHRoZSBkYXRlcyBlYWNoIHBsYXllciB3YXMgaW5qdXJlZA0KICBpbmp1cnlfZGF0ZXMgPC0gdW5pcXVlKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkNCiAgDQogIGZvcihqIGluIDE6bGVuZ3RoKGluanVyeV9kYXRlcykpew0KICAgICNjYWxjdWxhdGUgZGF0ZXMgZm9yIDEsIDIsIDMsIDQgd2Vla3MgYmVmb3JlIGFuZCBhZnRlciBlYWNoIGluanVyeSBkYXRlDQogICAgcGFzdF8xIDwtIGluanVyeV9kYXRlc1tqXS03DQogICAgcGFzdF8yIDwtIGluanVyeV9kYXRlc1tqXS0xNA0KICAgIHBhc3RfMyA8LSBpbmp1cnlfZGF0ZXNbal0tMjENCiAgICBwYXN0XzQgPC0gaW5qdXJ5X2RhdGVzW2pdLTI4DQogICAgZnV0dXJlXzEgPC0gaW5qdXJ5X2RhdGVzW2pdKzcNCiAgICBmdXR1cmVfMiA8LSBpbmp1cnlfZGF0ZXNbal0rMTQNCiAgICBmdXR1cmVfMyA8LSBpbmp1cnlfZGF0ZXNbal0rMjENCiAgICBmdXR1cmVfNCA8LSBpbmp1cnlfZGF0ZXNbal0rMjgNCiAgICANCiAgICAjQ2FsY3VsYXRpbmcgaG93IG1hbnkgaW5qdXJpZXMgdGhpcyBpcw0KICAgIGluanVyeV9jb3VudCA8LSBhcy5jaGFyYWN0ZXIoKGxlbmd0aChpbmp1cnlfZGF0ZXMpKSAtIGogKyAxKQ0KICAgIA0KICAgICNjb21wYXJlIGRhdGUgZm9yIGVhY2ggcGxheWVyIHRvIGRhdGUgb2YgaW5qdXJ5LCBzdG9yZSBpbiBXZWVrcy5BZnRlci5Jbmp1cnkgY29sdW1uLCBzdG9yZSBpbmp1cnkgY291bnQNCiAgICANCiAgICAjZmlyc3Qgd2VlayBhZnRlciBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMSxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiMSINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMSxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjc2Vjb25kIHdlZWsgYWZ0ZXIgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8xICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMixdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiMiINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzEgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8yLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICN0aGlyZCB3ZWVrIGFmdGVyIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMiAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzMsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIjMiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8yICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMyxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjZm91cnRoIHdlZWsgYWZ0ZXIgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8zICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfNCxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiNCINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzMgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV80LF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudCAgICANCiAgICAjd2VlayByaWdodCBiZWZvcmUgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IGluanVyeV9kYXRlc1tqXSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8xLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICItMSINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzEsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgI3R3byB3ZWVrcyBiZWZvcmUgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8yLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICItMiINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8xICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzIsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgI3RocmVlIHdlZWtzIGJlZm9yZSBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8yICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzMsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIi0zIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzIgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMyxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICAgICAgDQogICAgI2ZvdXIgd2Vla3MgYmVmb3JlIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzMgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfNCxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiLTQiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMyAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF80LF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICNEYXRlIG9mIEluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPT0gaW5qdXJ5X2RhdGVzW2pdLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICIwIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPT0gaW5qdXJ5X2RhdGVzW2pdLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICB9DQp9DQoNCg0KI21ha2luZyB3ZWVrcyBhZnRlciBpbmp1cnkgYW5kIGluanVyeSBjb3VudCBpbnRvIGEgZmFjdG9yIGFuZCBjb21iaW5pbmcgZGF0YSBzZXRzIGJhY2sgdG9nZXRoZXINCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nIDwtIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nICU+JQ0KICBtdXRhdGUoV2Vla3MuQWZ0ZXIuSW5qdXJ5ID0gZmFjdG9yKFdlZWtzLkFmdGVyLkluanVyeSksDQogICAgICAgICBJbmp1cnkuQ291bnQgPSBmYWN0b3IoSW5qdXJ5LkNvdW50KSkNCg0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIGxlZnRfam9pbihIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4sIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nKQ0KDQojZ2V0dGluZyByaWQgb2YganVuayB0aGF0IHdhcyBmcm9tIHRoZSBsb29wDQpyZW1vdmUoZnV0dXJlXzEsIGZ1dHVyZV8yLCBmdXR1cmVfMywgZnV0dXJlXzQsIGksIGluanVyeV9jb3VudCwgaW5qdXJ5X2RhdGVzLCBqLCBwYXN0XzEsIHBhc3RfMiwgcGFzdF8zLCBwYXN0XzQpDQpyZW1vdmUoSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcpDQpgYGANCg0KDQpgYGB7cn0NCmZvcihpIGluIDE6MjIpew0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1XZWVrcy5BZnRlci5Jbmp1cnkpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KSArDQogICAgbGFicyh0aXRsZT1pbmp1cmVkX0lEc1tpXSkrDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIi00Ij0iZ3JlZW4iLCAiLTMiPSJ5ZWxsb3ciLCAiLTIiPSJvcmFuZ2UiLCAiLTEiPSJyZWQiLCAiMCI9ImJsYWNrIiwiMSI9ICJwdXJwbGUiLCAiMiI9Im5hdnkiLCAiMyI9ImJsdWUiLCAiNCI9InNreWJsdWUiKSkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicyh0aXRsZT0iUnVubmluZyBJbWJhbGFuY2UiKQ0KICANCiAgcHJpbnQocCkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KI21ha2luZyBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgcmVsYXRpdmUgdG8gaW5qdXJ5DQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCBJbmp1cnkuQ291bnQsIFdlZWtzLkFmdGVyLkluanVyeSkgJT4lDQogIG11dGF0ZShXZWVrcy5BZnRlci5Jbmp1cnkuVmFyaWFiaWxpdHkgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpLA0KICAgICAgICAgV2Vla3MuQWZ0ZXIuSW5qdXJ5Lk1lYW4gPSBtZWFuKGFicyhSdW5uaW5nLkltYmFsYW5jZSkpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZm9yKGkgaW4gMToyMil7DQogICNsb29raW5nIGF0IG1lYW4gcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgYmVmb3JlIGFuZCBhZnRlciBpbmp1cnkgZm9yIGVhY2ggaW5qdXJlZCBwbGF5ZXINCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gaW5qdXJlZF9JRHNbaV0sXSwgYWVzKERhdGUsIFdlZWtzLkFmdGVyLkluanVyeS5NZWFuLCBncm91cD1Jbmp1cnkuQ291bnQpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9V2Vla3MuQWZ0ZXIuSW5qdXJ5KSkgKw0KICAgIHhsaW0obWluKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSktMzAsIG1heChJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpKzMwKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkgKw0KICAgIGxhYnModGl0bGU9aW5qdXJlZF9JRHNbaV0pICsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiLTQiPSJncmVlbiIsICItMyI9InllbGxvdyIsICItMiI9Im9yYW5nZSIsICItMSI9InJlZCIsICIwIj0iYmxhY2siLCIxIj0gInB1cnBsZSIsICIyIj0ibmF2eSIsICIzIj0iYmx1ZSIsICI0Ij0ic2t5Ymx1ZSIpKQ0KICANCiAgcHJpbnQocCkNCn0NCg0KYGBgDQoNCg0KYGBge3J9DQpmb3IoaSBpbiAxOjIyKXsNCiAgI2xvb2tpbmcgYXQgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgcGVyIHdlZWsgYmVmb3JlIGFuZCBhZnRlciBpbmp1cnkgZm9yIGVhY2ggaW5qdXJlZCBwbGF5ZXINCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gaW5qdXJlZF9JRHNbaV0sXSwgYWVzKERhdGUsIFdlZWtzLkFmdGVyLkluanVyeS5WYXJpYWJpbGl0eSwgZ3JvdXA9SW5qdXJ5LkNvdW50KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yPVdlZWtzLkFmdGVyLkluanVyeSkpICsNCiAgICB4bGltKG1pbihJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpLTMwLCBtYXgoSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KSszMCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpICsNCiAgICBsYWJzKHRpdGxlPWluanVyZWRfSURzW2ldKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIi00Ij0iZ3JlZW4iLCAiLTMiPSJ5ZWxsb3ciLCAiLTIiPSJvcmFuZ2UiLCAiLTEiPSJyZWQiLCAiMCI9ImJsYWNrIiwiMSI9ICJwdXJwbGUiLCAiMiI9Im5hdnkiLCAiMyI9ImJsdWUiLCAiNCI9InNreWJsdWUiKSkNCiAgDQogIHByaW50KHApDQp9DQpgYGANCg0KDQpgYGB7ciByZW1vdmluZyB1bm5lY2Vzc2FyeSBvYmplY3RzIGZyb20gc2VjdGlvbn0NCnJlbW92ZShpLCBwKQ0KYGBgDQoNCiMjIElzIHJ1bm5pbmcgaW1iYWxhbmNlIHNlbnNpdGl2ZSBlbm91Z2ggb2YgYSBtZXRyaWMgdG8gdXNlIGFzIGEgcHJvZ25vc2lzIHRvb2wgdmVyc3VzIGEgcmVoYWIgdG9vbD8NCg0KYGBge3J9DQpJbmNpZGVudF9SZXBvcnRfY2xlYW4gPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEluanVyeS5Qcm9nbm9zaXMpKSU+JQ0KICBtdXRhdGUoRXhwZWN0ZWQuU3RhcnQuUmV0dXJuID0gYXMuRGF0ZShpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09Ik5vIEV4cGVjdGVkIFRpbWUgTG9zcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSJMZXNzIHRoYW4gMSBXZWVrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iMS00IFdlZWtzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5K2RheXMoNyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDI4KSkpKSksDQogICAgICAgICBFeHBlY3RlZC5FbmQuUmV0dXJuID0gYXMuRGF0ZShpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09Ik5vIEV4cGVjdGVkIFRpbWUgTG9zcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSJMZXNzIHRoYW4gMSBXZWVrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cyg3KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSIxLTQgV2Vla3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cygyOCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDU2KSkpKSkpICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCBEYXRlLm9mLkluanVyeSkgJT4lDQogIG11dGF0ZShBY3R1YWwuUmV0dXJuID0gRGF0ZS5vZi5Jbmp1cnkrZGF5cyhzdW0oRGF5cy5pbi5TdGF0dXMpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KYGBge3J9DQpmb3IoaSBpbiAxOjIyKXsNCiAgI2xvb2tpbmcgYXQgbWVhbiBydW5uaW5nIGltYmFsYW5jZSBwZXIgd2VlayBiZWZvcmUgYW5kIGFmdGVyIGluanVyeSBmb3IgZWFjaCBpbmp1cmVkIHBsYXllcg0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBpbmp1cmVkX0lEc1tpXSxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UsIGdyb3VwPUluanVyeS5Db3VudCkpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aD0wLjEpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5LCBjb2xvcj0icmVkIikgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSRFeHBlY3RlZC5TdGFydC5SZXR1cm4sIGNvbG9yPSJwdXJwbGUiLCBsaW5ldHlwZT0zKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEV4cGVjdGVkLkVuZC5SZXR1cm4sIGNvbG9yPSJwdXJwbGUiLCBsaW5ldHlwZT0zKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEV4cGVjdGVkLkVuZC5SZXR1cm4sIGNvbG9yPSJwdXJwbGUiLCBsaW5ldHlwZT0xKSArDQogICAgbGFicyh0aXRsZT1pbmp1cmVkX0lEc1tpXSkNCiAgDQogIHByaW50KHApDQp9DQoNCmBgYA0KDQoNCg==